#include "stdafx.h"

#include "Player.h"

#include "Level.h"

namespace ETM
{
	Player::Player()
	{
		sprite = sf::Sprite();
		sprite.SetPosition(0, 0);
		jumpTime = 0.0f;
	}

	void Player::Load()
	{
		sprite.SetImage(*IM.GetResource("Images/player.png"));
	}

	void Player::Update(sf::View& view, float delta)
	{
		HandleInput();

		HandlePhysics(delta);

		movement = 0.0f;
		isJumping = false;

		view.SetCenter(sprite.GetPosition());
	}

	void Player::Render(sf::RenderWindow& window)
	{
		window.Draw(sprite);
	}

	void Player::HandleInput()
	{
		movement = 0.0f;

		if(Input::IsKeyDown(sf::Key::A) || Input::IsKeyDown(sf::Key::Left))
			movement = -1.0f;
		else if(Input::IsKeyDown(sf::Key::D) || Input::IsKeyDown(sf::Key::Right))
			movement = 1.0f;

		if(Input::IsKeyDown(sf::Key::Space))
			isJumping = true;
	}

	void Player::HandlePhysics(float delta)
	{
		sf::Vector2f prevPos = sprite.GetPosition();

		velocity.x += movement * 8000.0f * delta;
		velocity.y = Utils::Clamp(velocity.y + 2000.0f * delta, -600.0f, 600.0f);

		if(isOnGround)
			velocity.x *= 0.58f;
		else
			velocity.x *= 0.65f;

		velocity.x = Utils::Clamp(velocity.x, -2000.0f, 2000.0f);

		velocity.y = Jump(velocity.y, delta);

		sf::Vector2f pos = sprite.GetPosition();
		sprite.Move(velocity * delta);
		//Round position

		HandleCollision();

		pos = sprite.GetPosition();
		if(pos.x == prevPos.x)
			velocity.x = 0.0f;

		if(pos.y == prevPos.y)
			velocity.y = 0.0f;
	}

	void Player::HandleCollision()
	{
		sf::FloatRect bounds = GetBounds();
		int tileL = (int)floor(bounds.Left / Level::tileSize);
		int tileR = (int)ceil(bounds.Right / Level::tileSize) - 1;
		int tileT = (int)floor(bounds.Top / Level::tileSize);
		int tileB = (int)ceil(bounds.Bottom / Level::tileSize) - 1;

		if(tileL < 0 || tileT < 0 || tileR > currentLevel->GetWidth() - 1 || tileB > currentLevel->GetHeight() - 1)
			return;
	
		isOnGround = false;

		for(int y = tileT; y <= tileB; ++y)
		{
			for(int x = tileL; x <= tileR; ++x)
			{
				CollisionType collision = currentLevel->GetCollisionType(x, y);
				
				if(collision != Passable)
				{
					sf::FloatRect tileBounds = currentLevel->GetBounds(x, y);
					sf::Vector2f depth = Utils::GetIntersectionDepth(bounds, tileBounds);

					if(depth != sf::Vector2f(0, 0))
					{
						float absDepthX = abs(depth.x);
						float absDepthY = abs(depth.y);

						if(absDepthY < absDepthX)
						{
							if(previousBottom <= tileBounds.Top)
								isOnGround = true;

							if(collision == Impassable || isOnGround)
							{
								sf::Vector2f pos = sprite.GetPosition();
								sprite.SetPosition(pos.x, pos.y + depth.y);

								bounds = GetBounds();
							}
						}
						else if(collision == Impassable)
						{
							sf::Vector2f pos = sprite.GetPosition();
							sprite.SetPosition(pos.x + depth.x, pos.y);

							bounds = GetBounds();
						}
					}
				}
			}
		}

		previousBottom = bounds.Bottom;
	}

	float Player::Jump(float velocityY, float delta)
	{
		if(isJumping)
		{
			if((!wasJumping && isOnGround) || jumpTime > 0.0f)
				jumpTime += delta;

			if(jumpTime > 0.0f && jumpTime <= 0.35f)
			{
				velocityY = -2000.0f * (1.0f - pow(jumpTime / 0.35f, 0.14f));
			}
			else
			{
				jumpTime = 0.0f;
			}
		}
		else
		{
			jumpTime = 0.0f;
		}

		wasJumping = isJumping;

		return velocityY;
	}

	sf::FloatRect Player::GetBounds()
	{
		sf::Vector2f pos = sprite.GetPosition();
		sf::Vector2f size = sprite.GetSize();
		return sf::FloatRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y);
	}

	sf::Sprite& Player::GetSprite()
	{
		return sprite;
	}

	void Player::SetLevel(Level* level)
	{
		currentLevel = level;
	}

	void Player::SetPosition(float x, float y)
	{
		sprite.SetPosition(x, y);
	}
}